﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TusClient;

namespace UploadPhotoTool.Upload
{
    class UploadTask
    {
        #region 字段
        /// <summary>
        /// 上传文件列表
        /// </summary>
        private List<UploadFile> fUploadFiles = new List<UploadFile>();

        /// <summary>
        /// 后台任务
        /// </summary>
        private BackgroundWorker fBackgroundWorker;

        /// <summary>
        /// 上传服务器地址
        /// </summary>
        private string fServerUrl = string.Empty;

        /// <summary>
        /// 上传客户端
        /// </summary>
        private TusClient.TusClient fTusClient = null;
        #endregion

        #region 属性
        #region 正在上传文件序号
        private int currentIndex;

        public int CurrentIndex
        {
            get { return currentIndex; }
            set { currentIndex = value; }
        }
        #endregion

        #region 文件总数
        private int totalCount;

        public int TotalCount
        {
            get { return totalCount; }
            set { totalCount = value; }
        }
        #endregion

        #region 瞬时上传速度
        private string uploadSpeed;

        /// <summary>
        /// 瞬时上传速度
        /// </summary>
        public string UploadSpeed
        {
            get { return uploadSpeed; }
            set { uploadSpeed = value; }
        }
        #endregion

        #region 是否正在上传
        private bool uploading = false;

        /// <summary>
        /// 是否正在上传
        /// </summary>
        public bool Uploading
        {
            get { return uploading; }
            set { uploading = value; }
        }
        #endregion
        #endregion

        #region 构造函数
        public UploadTask(FileInfo[] files)
        {
            this.InitUploadTask(files);
            this.InitBackgroundWorker();
        }
        #endregion

        #region private方法
        #region 初始化任务
        /// <summary>
        /// 初始化任务
        /// </summary>
        /// <param name="files"></param>
        private void InitUploadTask(FileInfo[] files)
        {
            if (files != null)
            {
                foreach (var f in files)
                {
                    this.fUploadFiles.Add(new UploadFile(f));
                }
            }

            this.CurrentIndex = 1;
            this.TotalCount = files.Length;
            this.fServerUrl = "http://192.168.101.100:8081/big/upload/";
        }
        #endregion

        #region 初始化后台任务
        /// <summary>
        /// 初始化后台任务
        /// </summary>
        private void InitBackgroundWorker()
        {
            this.fBackgroundWorker = new BackgroundWorker();
            this.fBackgroundWorker.WorkerSupportsCancellation = true;
            this.fBackgroundWorker.DoWork += fBackgroundWorker_DoWork;
            this.fBackgroundWorker.RunWorkerCompleted += fBackgroundWorker_RunWorkerCompleted;
        }
        #endregion

        #region 上传进度
        /// <summary>
        /// 上传进度
        /// </summary>
        /// <param name="index"></param>
        /// <param name="file"></param>
        private void UploadWithProgress(int index, UploadFile file)
        {
            if (this.fBackgroundWorker.CancellationPending)
            {
                return;
            }

            var sw = new Stopwatch();
            long bytesTransferredLast = 0;
            double transferRate = 0;
            double previousPercentage = 0;
            try
            {
                this.Uploading = true;
                this.CurrentIndex = index;
                this.fTusClient = new TusClient.TusClient();
                this.fTusClient.Uploading += (long bytesTransferred, long bytesTotal) =>
                {
                    if (this.fBackgroundWorker.CancellationPending)
                    {
                        return;
                    }

                    if (sw.Elapsed.TotalSeconds > 0)
                    {
                        transferRate = (bytesTransferred - bytesTransferredLast) / sw.Elapsed.TotalSeconds;
                    }

                    double perc = Convert.ToDouble(bytesTransferred) / Convert.ToDouble(bytesTotal) * 100.0;
                    perc = Math.Truncate(perc);
                    if (perc != previousPercentage)
                    {
                        previousPercentage = perc;
                        this.UploadSpeed = this.HumanizeBytes(transferRate);
                        file.UploadedSize = bytesTransferred;
                        file.UploadProgress = previousPercentage;
                        file.UploadStatus = E_FileUploadStatus.上传中;
                        Console.WriteLine("已上传: {0:0.00}% {1} of {2} 瞬时上传速度：{3}/second", perc, this.HumanizeBytes(bytesTransferred), this.HumanizeBytes(bytesTotal), this.HumanizeBytes(transferRate));
                    }

                    // calc transfer rate over the last second
                    if (sw.Elapsed.TotalSeconds > 1)
                    {
                        bytesTransferredLast = bytesTransferred;
                        sw.Restart();
                    }

                    this.OnUploadStatusChanged(file);
                };

                var fileUrl = this.fTusClient.Create(this.fServerUrl, file.File);
                sw.Start();
                this.fTusClient.Upload(fileUrl, file.File);
                sw.Stop();

                file.UploadStatus = E_FileUploadStatus.已完成;
            }
            catch (TusException ex)
            {
                if (!this.fBackgroundWorker.CancellationPending)
                {
                    file.UploadStatus = E_FileUploadStatus.上传错误;
                    file.ErrorMessasge = ex.Message;
                }
                else
                {
                    file.UploadStatus = E_FileUploadStatus.已取消;
                }
            }
            catch (Exception ex)
            {
                if (!this.fBackgroundWorker.CancellationPending)
                {
                    file.UploadStatus = E_FileUploadStatus.上传错误;
                    file.ErrorMessasge = ex.Message;
                }
                else
                {
                    file.UploadStatus = E_FileUploadStatus.已取消;
                }
            }
            finally
            {
                this.OnUploadStatusChanged(file);
            }
        }
        #endregion

        #region 格式化文件大小
        /// <summary>
        /// 格式化文件大小
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private string HumanizeBytes(double bytes)
        {
            double res = bytes;
            if (res < 1024)
            {
                return string.Format("{0:n2} b", res);
            }

            res = res / 1024;
            if (res < 1024)
            {
                return string.Format("{0:n2} Kb", res);
            }

            res = res / 1024;
            if (res < 1024)
            {
                return string.Format("{0:n2} Mb", res);
            }

            res = res / 1024;
            return string.Format("{0:n2} Gb", res);
        }
        #endregion
        #endregion

        #region 事件处理
        #region 上传完成事件处理
        void fBackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.Uploading = false;
            this.fBackgroundWorker.Dispose();
            this.fBackgroundWorker = null;
            this.OnTaskComplated();
        }
        #endregion

        #region 上传处理
        void fBackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            int i = 1;
            foreach (var file in this.fUploadFiles)
            {
                this.UploadWithProgress(i++, file);
            }
        }
        #endregion
        #endregion

        #region public方法
        #region 开始上传
        /// <summary>
        /// 开始上传
        /// </summary>
        public void Start()
        {
            this.fBackgroundWorker.RunWorkerAsync();
        }
        #endregion

        #region 取消任务
        public void Cancel()
        {
            if (this.fTusClient != null)
            {
                this.fTusClient.Cancel();
            }

            if (this.fBackgroundWorker != null)
            {
                this.fBackgroundWorker.CancelAsync();
            }
        }
        #endregion
        #endregion

        #region 自定义事件
        #region 上传状态改变事件
        /// <summary>
        /// 上传状态改变事件
        /// </summary>
        /// <param name="sender">当前上传任务</param>
        /// <param name="file">正在上传的文件</param>
        public delegate void UploadStatusChangedHandle(UploadTask sender, UploadFile file);

        public event UploadStatusChangedHandle UploadStatusChanged;

        /// <summary>
        /// 上传状态改变事件
        /// </summary>
        /// <param name="file">正在上传的文件</param>
        public void OnUploadStatusChanged(UploadFile file)
        {
            if (this.UploadStatusChanged != null)
            {
                this.UploadStatusChanged(this, file);
            }
        }
        #endregion

        #region 任务完成事件
        public delegate void TaskComplatedHandle(UploadTask sender);

        public event TaskComplatedHandle TaskComplated;

        public void OnTaskComplated()
        {
            if (this.TaskComplated != null)
            {
                this.TaskComplated(this);
            }
        }
        #endregion
        #endregion
    }
}
